package cn.tedu._03vehicle.base.interceptors;

import io.swagger.v3.oas.models.security.SecurityScheme;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 网站限流拦截器:单个IP在1分钟之内访问网站的次数不超过3次;
 */
@Slf4j
public class RateLimitInterceptor implements HandlerInterceptor {
    //访问次数阈值、时间窗口大小
    private static final int MAX_REQUEST_COUNT = 3;
    private static final int MAX_REQUEST_WINDOW_SIZE = 60;

    //计数器:  {"192.168.1.11": 2, "192.168.1.12": 1, "192.168.1.13": 3}
    private final ConcurrentHashMap<String, Integer> clientCounts = new ConcurrentHashMap<>();
    //记录访问时间:  {"192.168.1.11": "2025-08-11 10:00:00", "192.168.1.12": "2025-08-11 11:00:00",  "192.168.1.13": "2025-08-11 09:00:00"}
    private final ConcurrentHashMap<String, LocalDateTime> lastResetTimes = new ConcurrentHashMap<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("进入到网站限流的拦截器...");
        String clientIp = request.getRemoteAddr(); //获取客户端的IP
        LocalDateTime lastTime = lastResetTimes.get(clientIp);
        /*
            1.该IP第一次访问,lastTime为null: 在两个HashMap中添加数据[计数器清零,时间为当前时间];
            2.该IP距离最后一次访问超过了60秒:  在两个HashMap中更新数据[计数器清零,时间为当前时间];
            3.该IP距离最后一次访问不超过60秒:  计数器+1,时间不变,再和3比较;
         */
        if (lastTime == null || LocalDateTime.now().isAfter(lastTime.plusSeconds(MAX_REQUEST_WINDOW_SIZE))){
            clientCounts.put(clientIp, 0);//计数器清零
            lastResetTimes.put(clientIp, LocalDateTime.now());//时间更新
        }
        //计数器+1
        Integer i = clientCounts.get(clientIp);
        i = i + 1;
        clientCounts.put(clientIp, i);
        //判断计数器是否超过阈值
        if (i > MAX_REQUEST_COUNT){
            throw new RuntimeException("访问过于频繁,请稍后再试...");
        }
        return true;
    }
}









